/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "log/Log.h"
#include "constant/PluginServiceCommon.h"
#include "obs_backup/ObjectStorageBackupJob.h"
#include "obs_restore/ObjectStorageRestoreJob.h"
#include "obs_index/ObjectStorageIndexJob.h"
#include "CommonJobFactory.h"

using namespace std::placeholders;
using namespace OBSPlugin;

namespace {
    constexpr auto MODULE = "CommonJobFactory";
    const int NUM_INT_1 = 1;
    const int NUM_INT_100 = 100;
}

CommonJobFactory::CommonJobFactory()
{
    m_commonJobMap[GetJobType(static_cast<int>(AppType::APPTYPE_OBJECT_STORAGE), JobType::BACKUP)] =
        JobFunc(std::bind(&CommonJobFactory::CreateFactoryJob<ObjectStorageBackupJob>, this, _1));
    m_commonJobMap[GetJobType(static_cast<int>(AppType::APPTYPE_OBJECT_STORAGE), JobType::RESTORE)] =
        JobFunc(std::bind(&CommonJobFactory::CreateFactoryJob<ObjectStorageRestoreJob>, this, _1));
    m_commonJobMap[GetJobType(static_cast<int>(AppType::APPTYPE_INDEX), JobType::INDEX)] =
        JobFunc(std::bind(&CommonJobFactory::CreateFactoryJob<ObjectStorageIndexJob>, this, _1));
}

CommonJobFactory::~CommonJobFactory() {}

uint64_t CommonJobFactory::GetJobType(uint64_t appType, JobType jobType)
{
    return appType * NUM_INT_100 + static_cast<uint64_t>(jobType) * NUM_INT_1;
}

uint64_t CommonJobFactory::GetAppType(const std::shared_ptr<JobCommonInfo>& jobCommonInfo, JobType jobType)
{
    if (jobType == JobType::BACKUP) {
        return GetBackupAppType(jobCommonInfo);
    }
    if (jobType == JobType::RESTORE) {
        return GetRestoreAppType(jobCommonInfo);
    }
    if (jobType == JobType::INDEX) {
        return GetIndexAppType(jobCommonInfo);
    }
    HCP_Log(ERR, MODULE) << "jobType is wrong ,the appType is: " << static_cast<int>(jobType) << HCPENDLOG;
    return static_cast<uint64_t>(AppType::APPTYPE_NONE);
}

uint64_t CommonJobFactory::GetBackupAppType(const std::shared_ptr<JobCommonInfo>& jobCommonInfo)
{
    std::shared_ptr<ThriftDataBase> tempPtr = jobCommonInfo->GetJobInfo();
    std::shared_ptr<AppProtect::BackupJob> jobInfo = std::dynamic_pointer_cast<AppProtect::BackupJob>(tempPtr);
    if (jobInfo == nullptr) {
        HCP_Log(ERR, MODULE) << "Pointer conversion failed., please check it! "<< HCPENDLOG;
        return static_cast<uint64_t>(AppType::APPTYPE_NONE);
    }
    if (jobInfo->jobParam.backupType == AppProtect::BackupJobType::FULL_BACKUP ||
        jobInfo->jobParam.backupType == AppProtect::BackupJobType::INCREMENT_BACKUP ||
        jobInfo->jobParam.backupType == AppProtect::BackupJobType::PERMANENT_INCREMENTAL_BACKUP) {
        return static_cast<uint64_t>(AppType::APPTYPE_OBJECT_STORAGE);
    }

    return static_cast<uint64_t>(AppType::APPTYPE_NONE);
}

uint64_t CommonJobFactory::GetRestoreAppType(const std::shared_ptr<JobCommonInfo>& jobCommonInfo)
{
    std::shared_ptr<ThriftDataBase> tempPtr = jobCommonInfo->GetJobInfo();
    std::shared_ptr<AppProtect::RestoreJob> jobInfo = std::dynamic_pointer_cast<AppProtect::RestoreJob>(tempPtr);
    if (jobInfo == nullptr) {
        HCP_Log(ERR, MODULE) << "Pointer conversion failed., please check it! "<< HCPENDLOG;
        return static_cast<uint64_t>(AppType::APPTYPE_NONE);
    }

    return static_cast<uint64_t>(AppType::APPTYPE_OBJECT_STORAGE);
}

uint64_t CommonJobFactory::GetIndexAppType(const std::shared_ptr<JobCommonInfo>& jobCommonInfo)
{
    DBGLOG("Enter ObsPlugin GetIndexAppType!");
    std::shared_ptr<ThriftDataBase> tempPtr = jobCommonInfo->GetJobInfo();
    std::shared_ptr<AppProtect::BuildIndexJob> jobInfo =
        std::dynamic_pointer_cast<AppProtect::BuildIndexJob>(tempPtr);
    if (jobInfo == nullptr) {
        ERRLOG("Pointer conversion failed!");
        return static_cast<uint64_t>(AppType::APPTYPE_NONE);
    }

    return static_cast<uint64_t>(AppType::APPTYPE_INDEX);
}

std::shared_ptr<BasicJob> CommonJobFactory::CreateJob(
    const std::shared_ptr<JobCommonInfo>& jobCommonInfo, JobType jobType)
{
    HCP_Log(DEBUG, MODULE) << "Enter create job." << HCPENDLOG;

    const uint64_t appType = GetAppType(jobCommonInfo, jobType);
    const uint64_t mapKey = GetJobType(appType, jobType);
    if (m_commonJobMap.find(mapKey) == m_commonJobMap.end()) {
        HCP_Log(ERR, MODULE) << "no such operation in map, create job failed" << HCPENDLOG;
        HCP_Log(ERR, MODULE) << "appType is: " << appType <<
            "\tcommandType is: " << static_cast<int>(jobType)  << HCPENDLOG;
        return nullptr;
    }
    DBGLOG("Create job appType: %d, mapKet: %d", appType, mapKey);
    auto func = m_commonJobMap[mapKey];
    DBGLOG("Func: %s", typeid(func).name());
    auto jobPtr = func(jobCommonInfo, appType);
    if (jobPtr == nullptr) {
        HCP_Log(ERR, MODULE) << "create job failed" << HCPENDLOG;
        return nullptr;
    }
    HCP_Log(DEBUG, MODULE) << "Exit create job." << HCPENDLOG;

    return jobPtr;
}

template<typename T>
std::shared_ptr<BasicJob> CommonJobFactory::CreateFactoryJob(std::shared_ptr<JobCommonInfo> jobCommonInfo)
{
    HCP_Log(DEBUG, MODULE) << "Enter create factory job." << HCPENDLOG;
    auto job = std::make_shared<T>();
    job->SetJobInfo(jobCommonInfo);
    return job;
}